/**
  ******************************************************************************
  * @file           : mp3_decode_stream.c
  * @version        : v0.1
  * @brief          : mp3_decode_stream Source file.
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
#include "mp3_def.h"
#include "mp3_decode_stream.h"
#include "heap.h"
#include "tim.h"

#define TIM_ARR_SAMPLE(X)    ((uint32_t)((TIMER_CLKSRC + (X>>1))/ (X) - 1))

typedef enum X2Buff_ID
{
    X2BUFF_0 = 0,
    X2BUFF_1
} X2Buff_ID;

static void start_a_dma_aligned();
static void moveOutput(X2Buff_ID id);
static void decode_to_pcm(RawBuf_t *raw, bool *needStart, bool isLast);

uint32_t SampleRates[] = {
    8000UL,
    11025UL,
    12000UL,
    16000UL,
    22025UL,
    24000UL,
    32000UL,
    44100UL,
    48000UL
};
uint32_t TimArrs[] = {
    TIM_ARR_SAMPLE(8000UL),
    TIM_ARR_SAMPLE(11025UL),
    TIM_ARR_SAMPLE(12000UL),
    TIM_ARR_SAMPLE(16000UL),
    TIM_ARR_SAMPLE(22025UL),
    TIM_ARR_SAMPLE(24000UL),
    TIM_ARR_SAMPLE(32000UL),
    TIM_ARR_SAMPLE(44100UL),
    TIM_ARR_SAMPLE(48000UL)
};

mp3_decode_stream_t *handler;

void mp3_set_read_fun(int (*f_read)(void *buf, unsigned int *length))
{
    handler->raw.f_read = f_read;
}

int mp3_decode_stream_init()
{
    handler = malloc(sizeof(mp3_decode_stream_t));
    if (handler == NULL)
        return -1;
    handler->decoder = MP3InitDecoder();
    if (handler->decoder == NULL) 
        return -1;
    memset(handler->x2buff, 0, sizeof(handler->x2buff));
    handler->x2SampleBuff[0] = 1633;
    handler->x2SampleBuff[1] = 1633;
    handler->raw.ptr = handler->raw.buf;
    handler->raw.remain = 0;
    mp3_hw_init();

    return 0;
}

/**
  * @brief 必定触发一个完成中断
  */
static inline void start_a_dma_aligned()
{
    RawBuf_t *raw = &handler->raw;
    if ((raw->ptr != raw->buf) && (raw->remain != 0))
    {
        mp3_start_data_DMA(raw->ptr, raw->buf, raw->remain);
    }
    else
    {
        setBufferAlignPending();
    }
}

/**
  * @brief 读取文件，填充raw buffer
  */
static inline void fill_rawbuf()
{
    RawBuf_t *raw;
    unsigned int br;
    if (!raw->is_fread_end && (raw->remain < RAW_DATA_SIZE))
    {
        raw = &handler->raw;
        br = RAW_DATA_SIZE - raw->remain;
        int fr = raw->f_read(&raw->buf[raw->remain], &br);
        raw->remain += br;
        if (fr)
        {
            raw->is_fread_end = true;
        }
    }
}

static uint32_t TimArr(uint32_t samplerate)
{
    for (int i = 0;i < sizeof(SampleRates) / sizeof(SampleRates[0]); i++)
    {
        if (SampleRates[i] == samplerate)
        {
            return TimArrs[i];
        }
    }
    return TimArrs[7];
}


/* XBUFF_REQUEST 优先级高，BUFFALIGN 优先级低 */


/**
  * @brief  raw Buff 持续同步
  *         raw_buf_state:
  *         0 准备完毕
  *         1 非同步对齐
  *         2 同步的对齐
  *         3 文件结束
  * 
  * @note  中断停止=rawBUF_EOF、rawBUF_READY
  */
void raw_buff_align_cplt_irq(DMA_HandleTypeDef *hdma)
{
    RawBuf_t *raw = &handler->raw;
    RawBuf_State_t state = raw->state;
    raw->ptr = raw->buf;

    if (state == RAWBUF_NOSYNC)
    {
        do
        {
            fill_rawbuf();

            if (!(raw->is_fread_end && (raw->remain == 0)))
            {
                int offset = MP3FindSyncWord((unsigned char*)raw->ptr, raw->remain);

                /* not find sync byte */
                if (offset < 0)
                {
                    raw->remain = 0;
                    continue;
                }
                
                raw->ptr += offset;
                raw->remain -= offset;
                raw->state = RAWBUF_SYNC;
                start_a_dma_aligned();
                break;
            }
            else
            {
                bool needStart;
                ENTER_CRITICAL();
                {
                    uint8_t status = handler->status;
                    if ((status & (STATUS_X2BUFF_0_REQUEST | STATUS_X2BUFF_1_REQUEST)) || !(status & STATUS_PCMBUFF_EXIST))
                    {
                        decode_to_pcm(raw, &needStart, true);
                    }
                    else
                    {
                        raw->state = RAWBUF_READY_EOF;
                    }
                }
                EXIT_CRITICAL();
                if (needStart)
                {
                    mp3_hw_start();
                }
                break;
            }
        } while (1);
    }
    else if (state == RAWBUF_SYNC)
    {
        int mp3_res;
        fill_rawbuf();
        mp3_res = MP3GetNextFrameInfo(handler->decoder, &handler->nextFrameinfo, (unsigned char*)raw->ptr);
        if (ERR_MP3_INVALID_FRAMEHEADER == mp3_res)
        {
            raw->ptr += 1;
            raw->remain -= 1;
            raw->state = RAWBUF_NOSYNC;
            start_a_dma_aligned();
            return;
        }
        else if (ERR_MP3_NONE == mp3_res)
        {
            #ifdef DEBUG
            printf("bitrate = %d \r\n", handler->nextFrameinfo.bitrate);
            printf("bitsPerSample = %d \r\n", handler->nextFrameinfo.bitsPerSample);
            printf("layer = %d \r\n", handler->nextFrameinfo.layer);
            printf("nChans = %d \r\n", handler->nextFrameinfo.nChans);
            printf("outputSamps = %d \r\n", handler->nextFrameinfo.outputSamps);
            printf("samprate = %d \r\n", handler->nextFrameinfo.samprate);
            printf("version = %d \r\n", handler->nextFrameinfo.version);
            #endif

            bool needStart;
            ENTER_CRITICAL();
            {
                uint8_t status = handler->status;
                if ((status & (STATUS_X2BUFF_0_REQUEST | STATUS_X2BUFF_1_REQUEST)) || !(status & STATUS_PCMBUFF_EXIST))
                {
                    decode_to_pcm(raw, &needStart, false);
                }
                else
                {
                    raw->state = RAWBUF_READY;
                }
            }
            EXIT_CRITICAL();
            if (needStart)
            {
                mp3_hw_start();
            }
        }
    }
    else if ((state == RAWBUF_READY) || (state == RAWBUF_READY_EOF))
    {
        bool needStart;
        ENTER_CRITICAL();
        {
            uint8_t status = handler->status;
            if ((status & (STATUS_X2BUFF_0_REQUEST | STATUS_X2BUFF_1_REQUEST)) || !(status & STATUS_PCMBUFF_EXIST))
            {
                decode_to_pcm(raw, &needStart, state == RAWBUF_READY_EOF);
            }
        }
        EXIT_CRITICAL();
        if (needStart)
        {
            mp3_hw_start();
        }
    }
}


/**
  * @brief  必须在 CRITICAL 中调用
  */
static void decode_to_pcm(RawBuf_t *raw, bool *needStart, bool isLast)
{
    {}
    EXIT_CRITICAL();
    int mp3_res;
    if (!isLast)
    {
        mp3_res = MP3Decode(handler->decoder, &raw->ptr, &raw->remain, handler->pcm, 0);
        if (mp3_res == ERR_MP3_NONE)
        {
            MP3GetLastFrameInfo(handler->decoder, &handler->frameinfo);
        }
    }
    else
    {
        mp3_res = ERR_MP3_NONE;
        memset(handler->pcm, 0, sizeof(handler->pcm));
    }
    ENTER_CRITICAL();

    if (!isLast)
    {
        raw->state = RAWBUF_NOSYNC;
        start_a_dma_aligned();
    }
    else
    {
        raw->state = RAWBUF_EOF;
    }

    *needStart = false;
    uint8_t status = handler->status;

    if (mp3_res == ERR_MP3_NONE)
    {
        if (status & (STATUS_X2BUFF_0_REQUEST | STATUS_X2BUFF_1_REQUEST))
        {
            if (status & STATUS_X2BUFF_0_REQUEST)
            {
                moveOutput(X2BUFF_0);
                handler->x2SampleBuff[0] = TimArr(handler->frameinfo.samprate);
            }
            else
            {
                moveOutput(X2BUFF_1);
                handler->x2SampleBuff[1] = TimArr(handler->frameinfo.samprate);
            }   
            handler->status = CLEAR_BIT(status, STATUS_X2BUFF_0_REQUEST | STATUS_X2BUFF_1_REQUEST);
            *needStart = true;
        }
        else
            SET_BIT(status, STATUS_PCMBUFF_EXIST);
    }
}

/**
  * @brief output->x2buff
  */
static void moveOutput(X2Buff_ID id)
{
    uint16_t *ptr;

    if (X2BUFF_0 == id)
    {
        ptr = handler->x2buff;
    }
    else if (X2BUFF_1 == id)
    {
        ptr = &handler->x2buff[LAY3_FRAME_SAMPLE_NUM * 2];
    }

    int count = handler->frameinfo.outputSamps;
    if (handler->frameinfo.nChans == 1)
    {
        for (int i = 0; i < count; i++)
        {
            ptr[i << 1] = (uint16_t)handler->pcm[i] ^ 0x8000U;
            ptr[(i << 1)+1] = (uint16_t)handler->pcm[i] ^ 0x8000U;
        }
    }
    else if (handler->frameinfo.nChans == 2)
    {
        for (int i = 0; i < count; i++)
        {
            ptr[i] = (uint16_t)handler->pcm[i] ^ 0x8000;
        }
    }
}

/**
  * @brief PCM数据转移
  */
void dma_dac_half_irq()
{
    /* pcmbuff 内有数据 */
    if (handler->status & STATUS_PCMBUFF_EXIST)
    {
        moveOutput(X2BUFF_0);
        handler->x2SampleBuff[0] = TimArr(handler->frameinfo.samprate);
        CLEAR_BIT(handler->status, STATUS_PCMBUFF_EXIST);
        if (handler->raw.state == RAWBUF_READY)
        {
            setBufferAlignPending();
        }
    }
    /* pcmbuff 内无数据 */
    else
    {
        /* stop tim */
        mp3_hw_pause();

        if (handler->raw.state == RAWBUF_EOF)
        {
            mp3_hw_stop();
            return;
        }

        SET_BIT(handler->status, STATUS_X2BUFF_0_REQUEST);
    }
}

void dma_dac_cplt_irq()
{
    /* pcmbuff 内有数据 */
    if (handler->status & STATUS_PCMBUFF_EXIST)
    {
        moveOutput(X2BUFF_1);
        handler->x2SampleBuff[1] = TimArr(handler->frameinfo.samprate);
        CLEAR_BIT(handler->status, STATUS_PCMBUFF_EXIST);
        if (handler->raw.state == RAWBUF_READY)
        {
            setBufferAlignPending();
        }
    }
    /* pcmbuff 内无数据 */
    else
    {
        /* stop tim */
        mp3_hw_pause();

        if (handler->raw.state == RAWBUF_EOF)
        {
            mp3_hw_stop();
            return;
        }

        SET_BIT(handler->status, STATUS_X2BUFF_1_REQUEST);
    }
}